﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AddIn.EA.Framework.Common;

namespace AddIn.EA.Framework
{
    /// <summary>
    /// Class that represent a list of attributes. Compose the Element class.
    /// </summary>
    public class AttributeList : BaseList<AttributeClass>
    {
        #region Constructors
        /// <summary>
        /// Get an instance of attribute list.
        /// </summary>
        /// <param name="reference">The reference of EA Object Model attribute collection</param>
        /// <param name="parent">The parent element</param>
        public AttributeList(EA_Application.Collection reference, IElement parent)
            : base(reference, parent) {  }
        #endregion
    }

    /// <summary>
    /// Class that represent the attributes of an element. Generally used to describe a internal variable of a class.
    /// </summary>
    public class AttributeClass : BaseElement<EA_Application.Attribute>
    {
        #region Fields
        private AttributeTagList _taggedValues;
        #endregion

        #region Constructors
        /// <summary>
        /// Get an instance o attribute.
        /// </summary>
        /// <param name="reference">The reference of EA Object Model attribute</param>
        public AttributeClass(EA_Application.Attribute reference)
            : this(reference, null)
        { }
        /// <summary>
        /// Get an instance o attribute.
        /// </summary>
        /// <param name="reference">The reference of EA Object Model attribute</param>
        /// <param name="parent">The parent element</param>
        public AttributeClass(EA_Application.Attribute reference, IElement parent)
            : base(reference, parent)
        { }
        #endregion

        #region Properties
        #region BaseElement Members
        /// <summary>
        /// The attribute ID
        /// </summary>
        public override int ID
        {
            get { return this.Reference.AttributeID; }
        }
        /// <summary>
        /// The attribute name
        /// </summary>
        public override string Name
        {
            get
            {
                return this.Reference.Name;
            }
            set
            {
                this.Reference.Name = value;
            }
        }
        #endregion

        /// <summary>
        /// The attribute alias. Used to decorate an object with C# standard.
        /// </summary>
        public string Alias
        {
            get { return this.Reference.Style; }
            set { this.Reference.Style = value; }
        }
        /// <summary>
        /// The attribute initial value.
        /// </summary>
        public string InitialValue
        {
            get { return this.Reference.Default; }
            set { this.Reference.Default = value; }
        }
        /// <summary>
        /// Flag used to indicate if the attribute is a constant.
        /// </summary>
        public bool IsConst
        {
            get { return this.Reference.IsConst; }
            set { this.Reference.IsConst = value; }
        }
        /// <summary>
        /// Flag used to indicate if the attribute is static.
        /// </summary>
        public bool IsStatic
        {
            get { return this.Reference.IsStatic; }
            set { this.Reference.IsStatic = value; }
        }
        /// <summary>
        /// Notes of the attribute.
        /// </summary>
        public string Notes
        {
            get { return this.Reference.Notes; }
            set { this.Reference.Notes = value; }
        }
        /// <summary>
        /// The attribute stereotype.
        /// </summary>
        public string Stereotype
        {
            get { return this.Reference.Stereotype; }
            set { this.Reference.Stereotype = value; }
        }
        /// <summary>
        /// The attribute type.
        /// </summary>
        public string Type
        {
            get { return this.Reference.Type; }
            set { this.Reference.Type = value; }
        }
        /// <summary>
        /// The attribute scope. Can be Public, Private, Protected or Internal.
        /// </summary>
        public ElementScope Scope
        {
            get
            {
                if (String.IsNullOrEmpty(this.Reference.Visibility))
                    return default(ElementScope);

                try
                {
                    return (ElementScope)Enum.Parse(typeof(ElementScope), this.Reference.Visibility, true);
                }
                catch { return default(ElementScope); }
            }

            set { this.Reference.Visibility = value.ToString(); }
        }
        /// <summary>
        /// A list of attribute tag. Generally used to indicate values for code generation.
        /// </summary>
        public AttributeTagList TaggedValues
        {
            get 
            {
                if (_taggedValues == null)
                    _taggedValues = new AttributeTagList(this.Reference.TaggedValues, this);

                return _taggedValues;
            }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Create a property based in this attribute automatically. Uses the Microsoft documentation pattern for .NET Framework 2.0.
        /// </summary>
        /// <example>
        /// <code>
        /// private string _property;
        /// 
        /// public string Property
        /// {
        ///     get { return _property; }
        ///     set { _property = value; }
        /// }
        /// </code>
        /// </example>
        public void CreateProperty()
        {
            CreateProperty(string.Empty);
        }
        /// <summary>
        /// Create a property based in this attribute automatically decorating the property with a C# attribute.
        /// Uses the Microsoft documentation pattern for .NET Framework 2.0.
        /// </summary>
        /// <param name="alias">Decoration of C# attribute.</param>
        /// <example>
        /// <remarks>
        /// Implementation:
        /// CreateProperty("[DataMember]");
        /// </remarks>
        /// <code>
        /// private string _property;
        /// 
        /// [DataMember]
        /// public string Property
        /// {
        ///     get { return _property; }
        ///     set { _property = value; }
        /// }
        /// </code>
        /// </example>
        public void CreateProperty(string alias)
        {
            try
            {
                Element element = (Element)this.Parent;

                Method propertyMethod = element.Methods.AddNew(ResolvePropertyName());
                propertyMethod.Alias = alias;
                propertyMethod.Notes = this.Notes;
                propertyMethod.ReturnType = this.Type;
                propertyMethod.Stereotype = "property";
                propertyMethod.Scope = ElementScope.Public;
                propertyMethod.Update();
                
                MethodTag tag = propertyMethod.TaggedValues.AddNew("attribute_name");
                tag.Value = this.Name;
                tag.Update();

                propertyMethod.TaggedValues.Refresh();

                element.Methods.Refresh();
            }
            catch(Exception ex)
            {
                throw new InvalidOperationException("Can't create property!", ex);
            }
        }

        /// <summary>
        /// Get a tag value from attribute by tag name.
        /// </summary>
        /// <param name="name">The tag value name.</param>
        /// <returns>The object tag value of attribute.</returns>
        public AttributeTag GetTaggedValueByName(string name)
        {
            return this.TaggedValues.GetReferenceByName(name);
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Transform an attribute name to property name using Microsoft pattern.
        /// </summary>
        /// <example>
        /// _property => Property
        /// </example>
        /// <returns>The property name.</returns>
        private string ResolvePropertyName()
        {
            string propertyName = null;

            if (this.Name.IndexOf("_") == 0)
                propertyName = string.Concat(this.Name.Substring(1, 1).ToUpper(), this.Name.Substring(2));
            else
                propertyName = string.Concat(this.Name.Substring(0, 1).ToUpper(), this.Name.Substring(1));

            return propertyName;
        }
        #endregion
    }

    /// <summary>
    /// Class that represent a list of attribute tagged values.
    /// </summary>
    public class AttributeTagList : BaseList<AttributeTag>
    {
        #region Constructors
        /// <summary>
        /// Get an instance of attribute tag value.
        /// </summary>
        /// <param name="reference">The reference of EA Object Model attribute tag value.</param>
        public AttributeTagList(EA_Application.Collection reference)
            : this(reference, null) { }
        /// <summary>
        /// Get an instance of attribute tag value.
        /// </summary>
        /// <param name="reference">The reference of EA Object Model attribute tag value.</param>
        /// <param name="parent">The parent element</param>
        public AttributeTagList(EA_Application.Collection reference, IElement parent)
            : base(reference, parent) { }
        #endregion
    }

    /// <summary>
    /// Class that represent an attribute tagged value.
    /// </summary>
    public class AttributeTag : BaseElement<EA_Application.AttributeTag>
    {
        #region Constructors
        /// <summary>
        /// Get an instance of attribute tag value.
        /// </summary>
        /// <param name="reference">The reference of EA Object Model attribute tag value.</param>
        public AttributeTag(EA_Application.AttributeTag reference)
            : this(reference, null) { }
        /// <summary>
        /// Get an instance of attribute tag value.
        /// </summary>
        /// <param name="reference">The reference of EA Object Model attribute tag value.</param>
        /// <param name="parent">The parent element.</param>
        public AttributeTag(EA_Application.AttributeTag reference, IElement parent)
            : base(reference, parent) { }
        #endregion

        #region Properties
        #region BaseElement Members
        /// <summary>
        /// The attribute tag value id.
        /// </summary>
        public override int ID
        {
            get { return this.Reference.TagID; }
        }
        /// <summary>
        /// The attribute tag value name.
        /// </summary>
        public override string Name
        {
            get
            {
                return this.Reference.Name;
            }
            set
            {
                this.Reference.Name = value;
            }
        }
        #endregion

        /// <summary>
        /// Notes for attribute tag value.
        /// </summary>
        public string Notes
        {
            get { return this.Reference.Notes; }
            set { this.Reference.Notes = value; }
        }
        /// <summary>
        /// The value of attribute tag value.
        /// </summary>
        public string Value
        {
            get { return this.Reference.Value; }
            set { this.Reference.Value = value; }
        }
        #endregion
    }
}
